Function.RegisterNamespace("System.Web.Event");

System.Web.Event.EventManager=function(){
    // Private members
    var _events;
    var _strategyManager;

    // Public Members
    this.Strategies;
        
    // ctor
    function EventManager(){
        _events=[];
        this.Strategies=_strategyManager=new System.Script.Strategy.StrategyManager(
            System.Web.EventStrategy.Standard,
            System.Web.EventStrategy.Redmond,
            System.Web.EventStrategy.Legacy
        );
    }
    EventManager.apply(this,arguments);

    // IDisposable Methods
    this.Dispose=function(){
        this.Clear();
        _strategyManager.Clear();
        this.Strategies=_events=_strategyManager=null;
    };
    
    // IEventStrategy Methods
    this.Add=function(node,type,handler){
        guardParameters("Add",node,type,handler);
        var evt={node:node,type:type,handler:handler};
        _events.push(evt);
        _strategyManager.Get(Object.Global()).Add(evt.node,evt.type,evt.handler);
    };

    this.Clear=function(){
        while(_events.length)removeHandler(_events.pop());
    };
    
    this.Fire=function(node,type,context){
        guardParameters("Fire",node,type);
        _strategyManager.Get(Object.Global()).Fire(node,type,context);
    };

    this.Get=function(node,type){
        guardParameters("Get",node,type);
        var context=Array.ForEach(_events,gatherHandler,handlerPredicate,{node:node,type:type,matches:[]});
        return context.matches;
    };

    this.GetOffset=function(event){
        var offset={x:event.offsetX||0,y:event.offsetY||0};
        if(typeof(event.offsetX)=="undefined"){
            var target=this.Target(event);
            var targetOffset=System.Web.Dom.GetOffset(target);
            offset.x=event.clientX-targetOffset.x;
            offset.y=event.clientY-targetOffset.y;
        }
        return offset;
    };
    
    this.Kill=function(event){
        return _strategyManager.Get(Object.Global()).Kill(event);
    };
    
    this.Remove=function(node,type,handler){
        guardParameters("Remove",node,type,handler);
        Array.ForEach(_events,removeHandler,handlerPredicate,{node:node,type:type,handler:handler});   
    };

    this.Suppress=function(event){
        return _strategyManager.Get(Object.Global()).Suppress(event);
    };

    this.Target=function(event){
        return _strategyManager.Get(Object.Global()).Target(event);
    };
    
    // Private Methods
    function gatherHandler(evt,context){
        context.matches.push(evt.handler);
    }
    
    function guardParameters(method,node,type,handler){
        if(node==undefined)throw new Error(String.Format("System.Web.Event.EventManager.{0}: 'node' must be a valid Node.",method));
        if(type==undefined||!Object.IsType(Function,type.toString))throw new Error(String.Format("System.Web.Event.EventManager.{0}: 'type' must be convertible to String.",method));
        if(arguments.length>3){
            if(!Object.IsType(Function,handler))throw new Error(String.Format("System.Web.Event.EventManager.{0}: 'handler' must be a valid Function pointer.",method));
        }
    }
    
    function handlerPredicate(evt,context){
        return evt.node==context.node&&evt.type==context.type&&(context.handler||evt.handler)==evt.handler;
    }

    function removeHandler(evt,context){
         _strategyManager.Get(Object.Global()).Remove(evt.node,evt.type,evt.handler);
        if(context)_events.splice(context.Index--,1);
    }
    
};

System.Web.Event.EventManager.Implement(System.Web.Event.IEventStrategy,"System.Web.Event.EventManager");